using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SexpSharp.Serialization;
using SexpSharp.Remoting;
using NUnit.Framework;

namespace SexpSharp.Serialization.Tests
{
    [TestFixture]
    public class Test1
    {
        [Test]
        public void test1() {
            deserialize("(serial-object #01# ())");
        }
        [Test]
        public void test2() {
            deserialize("(serial-object #01# (.b True))");
        }
        [Test]
        public void test3() {
            deserialize("(serial-object #01# (.b False))");
        }
        [Test]
        public void test4() {
            deserialize("(serial-object #01# (.u1 X))");
            deserialize("(serial-object #01# (.s1 X))");
        }
        [Test]
        public void test5() {
            deserialize("(serial-object #01# (.c XX))");
        }
        [Test]
        public void test6() {
            deserialize("(serial-object #01# (.u2 XX))");
            deserialize("(serial-object #01# (.s2 XX))");
        }
        [Test]
        public void test7() {
            deserialize("(serial-object #01# (.u4 XXXX))");
            deserialize("(serial-object #01# (.s4 XXXX))");
        }
        [Test]
        public void test8() {
            deserialize("(serial-object #01# (.u4 |YWJjZA==|))");
            deserialize("(serial-object #01# (.s4 |YWJjZA==|))");
        }
        [Test]
        public void test9() {
            deserialize("(serial-object #01# (.u4 #DEADBEEF#))");
            deserialize("(serial-object #01# (.s4 #DEADBEEF#))");
        }
        [Test]
        public void testa() {
            deserialize("(serial-object #01# (.u8 XXXXXXXX))");
            deserialize("(serial-object #01# (.s8 XXXXXXXX))");
        }
        [Test]
        public void testb() {
            deserialize("(serial-object #01# (.D \"3.14159\"))");
        }
        [Test]
        public void testbb() {
            deserialize("(serial-object #01# (.d \"3.14159\"))");
        }
        [Test]
        public void testbbb() {
            deserialize("(serial-object #01# (.f \"3.14159\"))");
        }
        [Test]
        public void testc() {
            deserialize("(serial-object #01# (.s \"this is a string\"))");
        }
        [Test]
        public void testd() {

            serialize(true);
        }
        [Test]
        public void teste() {
            serialize(false);
        }
        [Test]
        public void testf() {
            serialize('c');
        }
        [Test]
        public void testg() {
            serialize((byte) 0x1f);
        }
        [Test]
        public void testh() {
            serialize((ushort) 0xd00d);
        }
        [Test]
        public void testi() {
            serialize(0xcafebabe);
        }
        [Test]
        public void testj() {
            serialize(0xdeadcafebabebeef);
        }
        [Test]
        public void testk() {
            serialize(3.14159);
        }
        [Test]
        public void testl() {
            serialize("this is a string");
        }

        [Test]
        public void testm() {
            deserialize("(serial-object #01# (.arrayof .u1 #0123456789#))");
        }

        [Test]
        public void testn() {
            deserialize("(serial-object #01# (.arrayof .u2 #DEADBEEFCAFEBABE#))");
        }

        [Test]
        public void testo() {
            serialize(new int[] { 1, 2, 3, 4, 5 });
        }

		[Test]
		public void testp() {
			serialize(new object[] { new int[] { 1, 2, 3, 4 },
									 new bool[] { true, false, true, true },
									 new byte[] { 1, 2, 3, 4 },
									 new short[] { 1, 2, 3, 4 },
									 new long[] { 1, 2, 3, 4 } });
		}

		[Test]
		public void q() {
			deserialize("(serial-object #01# (.array ((.arrayof .s4 #00000001000000020000000300000004#) (.arrayof .b #01000101#) (.arrayof .u1 #01020304#) (.arrayof .s2 #0001000200030004#) (.arrayof .s8 |AAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQ=|))))");
		}

        [Test]
        public void r() {
            serialize(new object[] { null, null, 5 });
        }

        [Test]
        public void s() {
            deserialize("(serial-object #01# (.array (() () (.s4 #00000005#))))");
        }

        [Test]
        public void t() {
            SortedDictionary<string, int> foo = new SortedDictionary<string, int>();
            foo["one"] = 1;
            foo["two"] = 2;
            serialize(foo);
        }

		[Test]
		public void u() {
 			RemotableMethod<int, int> square = new RemotableMethod<int, int>(i => i * i);
			serialize(square, true);
		}

        static void deserialize(string expr)
        {
            MemoryStream s = new MemoryStream(new ASCIIEncoding().GetBytes(expr));
            object o = new SexpFormatter().Deserialize(s);
        }

		static void serialize(object o)
		{
			serialize(o, true);
		}

        static void serialize(object o, bool deser)
        {
            SexpFormatter f = new SexpFormatter();
            f.SerializeFormat = Format.Advanced;
            MemoryStream s = new MemoryStream();
			f.Serialize(Console.OpenStandardOutput(), o);
            f.Serialize(s, o);
			if (!deser)
				return;
            s.Position = 0;
            object o2 = f.Deserialize(s);
            if (o is Array)
            {
                Array a1 = (Array) o;
                Array a2 = (Array) o2;
                Assert.AreEqual(a1.Length, a2.Length);
                for (int i = 0; i < a1.Length; i++)
                    Assert.AreEqual(a1.GetValue(i), a2.GetValue(i));
            }
            else if (o is IDictionary)
            {
                IDictionary d1 = (IDictionary) o;
                IDictionary d2 = (IDictionary) o2;
                Assert.AreEqual(d1.Count, d2.Count);
                foreach (DictionaryEntry e in d1)
                {
                    Assert.AreEqual(e.Value, d2[e.Key]);
                }
            }
            else
                    Assert.AreEqual(o, o2);
        }
    }
}